/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 draw_terrain.h

	$Header: /heroes4/draw_terrain.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( DRAW_TERRAIN_H_INCLUDED )
#define DRAW_TERRAIN_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "abstract_bitmap_typedef.h"
#include "abstract_tile.h"
#include "adventure_tile_size.h"
#include "draw_adventure_tile.h"
#include "enumerate_tile_points_within_screen_rect.h"
#include "map_point.h"
#include "metatile_map.h"
#include "metatile_texture.h"
#include "metatile_texture_cache.h"
#include "quad.h"
#include "screen_point.h"
#include "terrain.h"
#include "transition_mask.h"
#include "transitions.h"

// ------------------------------------------------------------------------------
// Details namespace
// ------------------------------------------------------------------------------

namespace draw_terrain_details
{

	void draw_grid(
		t_quad< int > const &	height_quad,
		t_abstract_bitmap16 &	dest,
		t_screen_point const &	screen_pos,
		t_screen_rect const &	clip_rect );

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	inline bool is_terrain_type_overlapped( t_terrain_type terrain )
	{
		assert( terrain >= 0 && terrain < k_terrain_count );

		static bool const k_terrain_type_overlapped_array[ k_terrain_count ] =
		{
			true, //water
			true, //grass
			true, //rough
			true, //swamp
			true, //lava
			true, //snow
			true, //sand
			true, //dirt
			true, //subterranean
			true, //water_river
			true, //lava_river
			true, //ice_river
			false, //magic_all
			false, //magic_life
			false, //magic_order
			true, //magic_death
			true, //magic_chaos
			true, //magic_nature
			true, //might
		};

		return k_terrain_type_overlapped_array[ terrain ];
	}

	// ------------------------------------------------------------------------------
	// get_terrain_texture function template
	// ------------------------------------------------------------------------------
	template < typename t_terrain_map >
	t_adventure_tile_texture const & get_terrain_texture(
		t_terrain_map					terrain_map,
		t_terrain_type					terrain_type,
		int								terrain_subtype,
		t_level_map_point_2d const &	tile_point,
		t_map_point_2d const &			metatile_pos,
		t_uint16 const * &				palette )
	{
		t_level_map_point_2d metatile_point( metatile_pos, tile_point.level );

		// Get the metatile offset
		t_metatile_map const & metatile_map = terrain_map.get_metatile_map();
		t_metatile_map::t_metatile_info const & metatile_info = metatile_map.get_metatile_info( metatile_point );
		t_map_point_2d metatile_offset =	is_terrain_type_overlapped( terrain_type )
												? metatile_info.get_offset()
												: t_map_point_2d( k_metatile_max_offset / 2, k_metatile_max_offset / 2 );

		// Get the metatile texture
		int texture_num = metatile_info.get_texture_num( terrain_type, terrain_subtype );
		t_metatile_texture_ptr metatile_texture_ptr = get_metatile_texture( terrain_type, terrain_subtype, texture_num );

		t_metatile_texture const & metatile_texture = *metatile_texture_ptr;

		// Compute the tile position within the metatile to draw
		t_map_point_2d tile_offset( tile_point );

		tile_offset.row -=	(	metatile_point.row * k_metatile_logical_size
							-	k_metatile_texture_border_size
							-	k_metatile_max_offset 
							+	metatile_offset.row );
		tile_offset.column -=	(	metatile_point.column * k_metatile_logical_size
								-	k_metatile_texture_border_size
								-	k_metatile_max_offset
								+	metatile_offset.column );

		// Get the adventure tile data
		palette = metatile_texture.get_palette();
		return metatile_texture.get_adventure_tile_texture( tile_offset );
	}

	// ------------------------------------------------------------------------------
	// get_road_texture function template
	// ------------------------------------------------------------------------------
	template < typename t_terrain_map >
	t_adventure_tile_texture const & get_road_texture(
		t_terrain_map					terrain_map,
		t_road_type						road_type,
		t_level_map_point_2d const &	tile_point,
		t_map_point_2d const &			metatile_pos,
		t_uint16 const * &				palette )
	{
		t_level_map_point_2d metatile_point( metatile_pos, tile_point.level );

		// Get the metatile offset
		t_metatile_map const & metatile_map = terrain_map.get_metatile_map();
		t_metatile_map::t_metatile_info const & metatile_info = metatile_map.get_metatile_info( metatile_point );
		t_map_point_2d metatile_offset = metatile_info.get_offset();

		// Get the metatile texture
		int texture_num = metatile_info.get_texture_num( road_type );
		t_metatile_texture_ptr metatile_texture_ptr = get_metatile_texture( road_type, texture_num );

		t_metatile_texture const & metatile_texture = *metatile_texture_ptr;

		// Compute the tile position within the metatile to draw
		t_map_point_2d tile_offset( tile_point );

		tile_offset.row -=	(	metatile_point.row * k_metatile_logical_size
							-	k_metatile_texture_border_size
							-	k_metatile_max_offset 
							+	metatile_offset.row );
		tile_offset.column -=	(	metatile_point.column * k_metatile_logical_size
								-	k_metatile_texture_border_size
								-	k_metatile_max_offset
								+	metatile_offset.column );

		// Get the adventure tile data
		palette = metatile_texture.get_palette();
		return metatile_texture.get_adventure_tile_texture( tile_offset );
	}

	// ------------------------------------------------------------------------------
	// draw_tile_layer function template
	// ------------------------------------------------------------------------------

	template < typename t_terrain_map >
	void draw_tile_layer(
		t_terrain_map					terrain_map,
		t_level_map_point_2d const &	tile_point,
		t_terrain_type					terrain_type,
		int								terrain_subtype,
		t_composite_tile_texture &		dest,
		t_screen_point const &			screen_pos,
		t_screen_rect const &			clip_rect )
	{
		int left = clip_rect.left - screen_pos.x;
		if ( left < 0 )
			left = 0;

		int right = clip_rect.right - screen_pos.x;
		if ( right > k_adventure_tile_width )
			right = k_adventure_tile_width;

		if ( is_terrain_type_overlapped( terrain_type ) )
		{
			// Get the vector of metatile positions which overlap this tile
			t_metatile_map const & metatile_map = terrain_map.get_metatile_map();
			t_metatile_map::t_tile_info const & tile_metatile_info = metatile_map.get_tile_info( tile_point );

			// Draw each metatile
			int intersecting_metatile_count = tile_metatile_info.get_intersecting_metatile_count();
			int intersecting_metatile_num;
			for (	intersecting_metatile_num = 0;
					intersecting_metatile_num < intersecting_metatile_count;
					++intersecting_metatile_num )
			{
				t_map_point_2d metatile_point = tile_metatile_info.get_intersecting_metatile_point( intersecting_metatile_num );

				// Get the adventure tile data
				t_uint16 const * palette;
				t_adventure_tile_texture const & adventure_tile_texture =
					get_terrain_texture< t_terrain_map >( terrain_map, terrain_type, terrain_subtype, tile_point, metatile_point, palette );

				// Draw it
				draw_adventure_tile( adventure_tile_texture, palette, dest, left, right );
			}
		}
		else
		{
			t_map_point_2d metatile_point = tile_point / k_metatile_logical_size;

			// Get the adventure tile data
			t_uint16 const * palette;
			t_adventure_tile_texture const & adventure_tile_texture =
				get_terrain_texture< t_terrain_map >( terrain_map, terrain_type, terrain_subtype, tile_point, metatile_point, palette );

			// Draw it
			draw_adventure_tile( adventure_tile_texture, palette, dest, left, right );
		}
	}

	// ------------------------------------------------------------------------------
	// draw_tile_layer function template
	// ------------------------------------------------------------------------------

	template < typename t_terrain_map >
	void draw_tile_layer(
		t_terrain_map					terrain_map,
		t_level_map_point_2d const &	tile_point,
		t_terrain_type					terrain_type,
		int								terrain_subtype,
		t_quad< int > const &			height_quad,
		t_quad< int > const &			lighting_quad,
		t_abstract_bitmap16 &			dest,
		t_screen_point const &			screen_pos,
		t_screen_rect const &			clip_rect )
	{
		if ( is_terrain_type_overlapped( terrain_type ) )
		{
			// Get the vector of metatile positions which overlap this tile
			t_metatile_map const & metatile_map = terrain_map.get_metatile_map();
			t_metatile_map::t_tile_info const & tile_metatile_info = metatile_map.get_tile_info( tile_point );

			// Draw each metatile
			int intersecting_metatile_count = tile_metatile_info.get_intersecting_metatile_count();
			int intersecting_metatile_num;
			for (	intersecting_metatile_num = 0;
					intersecting_metatile_num < intersecting_metatile_count;
					++intersecting_metatile_num )
			{
				t_map_point_2d metatile_point = tile_metatile_info.get_intersecting_metatile_point( intersecting_metatile_num );

				// Get the adventure tile data
				t_uint16 const * palette;
				t_adventure_tile_texture const & adventure_tile_texture =
					get_terrain_texture< t_terrain_map >( terrain_map, terrain_type, terrain_subtype, tile_point, metatile_point, palette );

				// Draw it
				draw_adventure_tile( adventure_tile_texture, palette, dest, screen_pos, clip_rect, height_quad, lighting_quad );
			}
		}
		else
		{
			t_map_point_2d metatile_point = tile_point / k_metatile_logical_size;

			// Get the adventure tile data
			t_uint16 const * palette;
			t_adventure_tile_texture const & adventure_tile_texture =
				get_terrain_texture< t_terrain_map >( terrain_map, terrain_type, terrain_subtype, tile_point, metatile_point, palette );

			// Draw it
			draw_adventure_tile( adventure_tile_texture, palette, dest, screen_pos, clip_rect, height_quad, lighting_quad );
		}
	}

	// ------------------------------------------------------------------------------
	// draw_terrain_transitions function template
	// ------------------------------------------------------------------------------
	
	template < typename t_terrain_map >
	void draw_terrain_transitions(
		t_terrain_map					terrain_map,
		t_level_map_point_2d const &	tile_point,
		t_abstract_tile const &			tile,
		t_quad< int > const &			height_quad,
		t_quad< int > const &			lighting_quad,
		t_abstract_bitmap16 &			dest,
		t_screen_point const &			screen_pos,
		t_screen_rect const &			clip_rect )
	{
		// Draw each of the transitions in this tile position
		int num_transitions = tile.get_terrain_transition_count();
		if ( num_transitions <= 0 )
			return;

		t_metatile_map const & metatile_map = terrain_map.get_metatile_map();
		t_metatile_map::t_tile_info const & tile_metatile_info = metatile_map.get_tile_info( tile_point );
		int intersecting_metatile_count = tile_metatile_info.get_intersecting_metatile_count();

		int transition_num;
		for ( transition_num = 0; transition_num < num_transitions; ++transition_num )
		{
			// Get the transition mask
			t_terrain_transition const & transition = tile.get_terrain_transition( transition_num );
			t_transition_mask const & mask = g_transition_masks[ transition.get_mask_set() ][ transition.get_id() ];
			t_terrain_type transition_terrain = transition.get_terrain();

			if ( is_terrain_type_overlapped( transition_terrain ) )
			{
				// check if this requires alpha blending, and we are going to draw 2+ metatiles.
				if ( mask.get_bits() == 4 && intersecting_metatile_count > 1 )
				{
					// in this case, we must use an intermediate buffer.
					// draw the terrain to the intermediate buffer.
					static t_composite_tile_texture composite_buffer;

					draw_tile_layer< t_terrain_map >(
						terrain_map, tile_point, transition_terrain, transition.get_terrain_subtype(),
						composite_buffer, screen_pos, clip_rect );

					// draw transition mask
					draw_adventure_tile( composite_buffer, dest, screen_pos, clip_rect, height_quad, lighting_quad, mask );
				}
				else
				{
					// Draw each metatile
					int intersecting_metatile_num;
					for (	intersecting_metatile_num = 0;
							intersecting_metatile_num < intersecting_metatile_count;
							++intersecting_metatile_num )
					{
						t_map_point_2d metatile_point = tile_metatile_info.get_intersecting_metatile_point( intersecting_metatile_num );

						// Get the adventure tile data
						t_uint16 const * palette;
						t_adventure_tile_texture const & adventure_tile_texture	=
							get_terrain_texture< t_terrain_map >(
								terrain_map, transition_terrain, transition.get_terrain_subtype(),
								tile_point, metatile_point, palette );

						// Draw it masked
						draw_adventure_tile(
							adventure_tile_texture, palette, dest, screen_pos, clip_rect, height_quad, lighting_quad, mask );
					}
				}
			}
			else
			{
				t_map_point_2d metatile_pos = tile_point / k_metatile_logical_size;

				// Get the adventure tile data
				t_uint16 const * palette;
				t_adventure_tile_texture const & adventure_tile_texture	=
					get_terrain_texture< t_terrain_map >(
						terrain_map, transition_terrain, transition.get_terrain_subtype(),
						tile_point, metatile_pos, palette );

				// Draw it masked
				draw_adventure_tile(
					adventure_tile_texture, palette, dest, screen_pos, clip_rect, height_quad, lighting_quad, mask );
			}
		}
	}

	// ------------------------------------------------------------------------------
	// draw_tile_layer function template
	// ------------------------------------------------------------------------------

	template < typename t_terrain_map >
	void draw_road_layer(
		t_terrain_map					terrain_map,
		t_level_map_point_2d const &	tile_point,
		t_road_type						road_type,
		t_composite_tile_texture &		dest,
		t_screen_point const &			screen_pos,
		t_screen_rect const &			clip_rect )
	{
		int left = clip_rect.left - screen_pos.x;
		if ( left < 0 )
			left = 0;

		int right = clip_rect.right - screen_pos.x;
		if ( right > k_adventure_tile_width )
			right = k_adventure_tile_width;

		// Get the vector of metatile positions which overlap this tile
		t_metatile_map const & metatile_map = terrain_map.get_metatile_map();
		t_metatile_map::t_tile_info const & tile_metatile_info = metatile_map.get_tile_info( tile_point );

		// Draw each metatile
		int intersecting_metatile_count = tile_metatile_info.get_intersecting_metatile_count();
		int intersecting_metatile_num;
		for (	intersecting_metatile_num = 0;
				intersecting_metatile_num < intersecting_metatile_count;
				++intersecting_metatile_num )
		{
			t_map_point_2d metatile_point = tile_metatile_info.get_intersecting_metatile_point( intersecting_metatile_num );

			// Get the adventure tile data
			t_uint16 const * palette;
			t_adventure_tile_texture const & adventure_tile_texture =
				get_road_texture< t_terrain_map >( terrain_map, road_type, tile_point, metatile_point, palette );

			// Draw it
			draw_adventure_tile( adventure_tile_texture, palette, dest, left, right );
		}
	}

	// ------------------------------------------------------------------------------
	// draw_road function_template
	// ------------------------------------------------------------------------------

	template < typename t_terrain_map >
	void draw_road(
		t_terrain_map					terrain_map,
		t_level_map_point_2d const &	tile_point,
		t_abstract_tile const &			tile,
		t_quad< int > const &			height_quad,
		t_quad< int > const &			lighting_quad,
		t_abstract_bitmap16 &			dest,
		t_screen_point const &			screen_pos,
		t_screen_rect const &			clip_rect )
	{
		// Draw the road here if any
		t_road_type road_type = tile.get_road_type();

		if ( road_type == k_road_none )
		{
			int num_transitions = tile.get_road_transition_count();
			if ( num_transitions > 0 )
			{
				t_metatile_map const & metatile_map = terrain_map.get_metatile_map();
				t_metatile_map::t_tile_info const & tile_metatile_info = metatile_map.get_tile_info( tile_point );
				int intersecting_metatile_count = tile_metatile_info.get_intersecting_metatile_count();

				int transition_num;
				for ( transition_num = 0; transition_num < num_transitions; ++transition_num )
				{
					// Get the transition mask
					t_road_transition const & transition = tile.get_road_transition( transition_num );
					t_transition_mask const & mask = g_transition_masks[ transition.get_mask_set() ][ transition.get_id() ];
					t_road_type transition_road_type = transition.get_road_type();
					assert( transition_road_type != k_road_none );
					assert( mask.get_bits() == 1 );

					// Draw each metatile
					int intersecting_metatile_num;
					for (	intersecting_metatile_num = 0;
							intersecting_metatile_num < intersecting_metatile_count;
							++intersecting_metatile_num )
					{
						t_map_point_2d metatile_point = tile_metatile_info.get_intersecting_metatile_point( intersecting_metatile_num );

						// Get the adventure tile data
						t_uint16 const * palette;
						t_adventure_tile_texture const & adventure_tile_texture	=
							get_road_texture< t_terrain_map >(
								terrain_map, transition_road_type, tile_point, metatile_point, palette );

						// Draw it masked
						draw_adventure_tile(
							adventure_tile_texture, palette, dest, screen_pos, clip_rect, height_quad, lighting_quad, mask );
					}
				}
			}
		}
		else
		{
			t_metatile_map const & metatile_map = terrain_map.get_metatile_map();
			t_metatile_map::t_tile_info const & tile_metatile_info = metatile_map.get_tile_info( tile_point );
			int intersecting_metatile_count = tile_metatile_info.get_intersecting_metatile_count();

			int num_transitions = tile.get_road_transition_count();
			if ( num_transitions <= 0 )
			{
				// Draw each metatile
				int intersecting_metatile_num;
				for (	intersecting_metatile_num = 0;
						intersecting_metatile_num < intersecting_metatile_count;
						++intersecting_metatile_num )
				{
					t_map_point_2d metatile_point = tile_metatile_info.get_intersecting_metatile_point( intersecting_metatile_num );

					// Get the adventure tile data
					t_uint16 const * palette;
					t_adventure_tile_texture const & adventure_tile_texture =
						get_road_texture< t_terrain_map >( terrain_map, road_type, tile_point, metatile_point, palette );

					// Draw it
					draw_adventure_tile( adventure_tile_texture, palette, dest, screen_pos, clip_rect, height_quad, lighting_quad );
				}
			}
			else
			{
				static t_composite_tile_texture		composite_buffer;
				static t_composite_transition_mask	composite_mask;

				int left = clip_rect.left - screen_pos.x;
				if ( left < 0 )
					left = 0;

				int right = clip_rect.right - screen_pos.x;
				if ( right > k_adventure_tile_width )
					right = k_adventure_tile_width;

				// Initialize the affected region of the composite mask
				int column;
				for ( column = left; column < right; ++column )
					composite_mask.get_column_mask( column ) = ~t_uint32( 0 );

				// Draw each metatile
				int intersecting_metatile_num;
				for (	intersecting_metatile_num = 0;
						intersecting_metatile_num < intersecting_metatile_count;
						++intersecting_metatile_num )
				{
					t_map_point_2d metatile_point = tile_metatile_info.get_intersecting_metatile_point( intersecting_metatile_num );

					// Get the adventure tile data
					t_uint16 const * palette;
					t_adventure_tile_texture const & adventure_tile_texture =
						get_road_texture< t_terrain_map >( terrain_map, road_type, tile_point, metatile_point, palette );

					// Draw it
					draw_adventure_tile( adventure_tile_texture, palette, composite_buffer, left, right );
				}

				// Now draw each of the transitions
				int transition_num;
				for ( transition_num = 0; transition_num < num_transitions; ++transition_num )
				{
					// Get the transition mask
					t_road_transition const & transition = tile.get_road_transition( transition_num );
					t_transition_mask const & mask = g_transition_masks[ transition.get_mask_set() ][ transition.get_id() ];
					t_road_type transition_road_type = transition.get_road_type();

					if ( transition_road_type != k_road_none )
					{
						// check if this requires alpha blending, and we are going to draw 2+ metatiles.
						if ( mask.get_bits() == 4 && intersecting_metatile_count > 1 )
						{
							// in this case, we must use an intermediate buffer.
							// draw the terrain to the intermediate buffer.
							static t_composite_tile_texture inner_composite_buffer;

							draw_road_layer< t_terrain_map >(
								terrain_map, tile_point, transition_road_type, inner_composite_buffer, screen_pos, clip_rect );

							// draw transition mask
							draw_adventure_tile( inner_composite_buffer, composite_buffer, left, right, mask );
						}
						else
						{
							// Draw each metatile
							int intersecting_metatile_num;
							for (	intersecting_metatile_num = 0;
									intersecting_metatile_num < intersecting_metatile_count;
									++intersecting_metatile_num )
							{
								t_map_point_2d metatile_point = tile_metatile_info.get_intersecting_metatile_point( intersecting_metatile_num );

								// Get the adventure tile data
								t_uint16 const * palette;
								t_adventure_tile_texture const & adventure_tile_texture	=
									get_road_texture< t_terrain_map >(
										terrain_map, transition_road_type, tile_point, metatile_point, palette );

								// Draw it masked
								draw_adventure_tile(
									adventure_tile_texture, palette, composite_buffer, left, right, mask );
							}
						}
					}
					else
					{
						assert( mask.get_bits() == 1 );

						// Exclude the transition mask from our composite mask
						for ( column = left; column < right; ++column )
							composite_mask.get_column_mask( column ) &= ~mask.get_column_mask( column );
					}
				}

				draw_adventure_tile( composite_buffer, dest, screen_pos, clip_rect, height_quad, lighting_quad, composite_mask );
			}
		}
	}

	// ------------------------------------------------------------------------------
	// draw_tile function template
	// ------------------------------------------------------------------------------

	template < typename t_terrain_map >
	void draw_tile(
		t_terrain_map					terrain_map,
		t_level_map_point_2d const &	tile_point,
		t_abstract_bitmap16 &			dest,
		t_screen_point const &			screen_pos,
		t_screen_rect const &			clip_rect,
		bool							draw_grid )
	{
		// Get the tile to draw
		t_abstract_tile const & tile = terrain_map.get_tile( tile_point );
		t_quad< t_abstract_tile_vertex const & > tile_vertex_quad = terrain_map.get_tile_vertex_quad( tile_point );

		t_quad< int >	height_quad(
							tile_vertex_quad.left.get_height(), tile_vertex_quad.top.get_height(),
							tile_vertex_quad.right.get_height(), tile_vertex_quad.bottom.get_height() );

		t_quad< int >	lighting_quad(
							tile_vertex_quad.left.get_light(), tile_vertex_quad.top.get_light(),
							tile_vertex_quad.right.get_light(), tile_vertex_quad.bottom.get_light() );

		if (	tile.get_rock_terrain_type() == k_rock_terrain_none
			||	tile.has_rock_terrain_transition() )
		{
			if (	tile.get_road_type() == k_road_none
				||	tile.get_road_transition_count() > 0 )
			{
				draw_tile_layer< t_terrain_map >(
					terrain_map, tile_point, tile.get_terrain(), tile.get_terrain_subtype(),
					height_quad, lighting_quad, dest, screen_pos, clip_rect );

				draw_terrain_transitions< t_terrain_map >(
					terrain_map, tile_point, tile, height_quad, lighting_quad, dest, screen_pos, clip_rect );
			}

			draw_road< t_terrain_map >(
				terrain_map, tile_point, tile, height_quad, lighting_quad, dest, screen_pos, clip_rect );

			if ( draw_grid )
				draw_terrain_details::draw_grid( height_quad, dest, screen_pos, clip_rect );
		}
		else
			fill_adventure_tile( t_uint16( 0 ), dest, screen_pos, clip_rect, height_quad );
	}

	// ------------------------------------------------------------------------------
	// t_draw_tile_func class template
	// ------------------------------------------------------------------------------

	template < typename t_terrain_map >
	class t_draw_tile_func
	{
	public:
		// Constructor
		t_draw_tile_func(
			t_abstract_bitmap16 &	dest,
			bool					draw_grid );

		// Operator
		void	operator()(
					t_terrain_map					terrain_map,
					t_level_map_point_2d const &	tile_point,
					t_screen_point const &			screen_pos,
					t_screen_rect const &			clip_rect ) const;

	private:
		// Data members
		t_abstract_bitmap16 &	m_dest;
		bool					m_draw_grid;
	};

	// ------------------------------------------------------------------------------
	// t_draw_tile_func members
	// ------------------------------------------------------------------------------

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	template < typename t_terrain_map >
	inline t_draw_tile_func< t_terrain_map >::t_draw_tile_func(
		t_abstract_bitmap16 &	dest,
		bool					draw_grid )
		:	m_dest( dest ),
			m_draw_grid( draw_grid )
	{
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	template < typename t_terrain_map >
	inline void t_draw_tile_func< t_terrain_map >::operator()(
		t_terrain_map					terrain_map,
		t_level_map_point_2d const &	tile_point,
		t_screen_point const &			screen_pos,
		t_screen_rect const &			clip_rect ) const
	{
		draw_tile< t_terrain_map >(
			terrain_map,
			tile_point,
			m_dest,
			screen_pos,
			clip_rect,
			m_draw_grid );
	}

} // Details namespace

// ------------------------------------------------------------------------------
// draw_terrain function template - abstracts the process of drawing terrain.
// The t_terrain_map template argument must supply the following members:
//
// t_metatile_map const &					get_metatile_map()
// t_screen_point							get_screen_point( t_map_point_2d )
// int										get_size()
// t_abstract_tile const &					get_tile( t_level_map_point_2d )
// t_quad< t_abstract_tile_vertex const & >	get_tile_vertex_quad( t_level_map_point_2d )
// t_screen_point							get_view_size()
// bool										is_valid( t_map_point_2d )
// ------------------------------------------------------------------------------

template < typename t_terrain_map >
void draw_terrain(
	t_terrain_map			terrain_map,
	int						level,
	t_screen_rect const &	src_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_pos,
	bool					draw_grid )
{
	using namespace draw_terrain_details;

	enumerate_tile_points_within_screen_rect< t_terrain_map >(
		terrain_map,
		level,
		src_rect,
		dest_pos,
		t_draw_tile_func< t_terrain_map >( dest, draw_grid ) );
}

#endif // !defined( DRAW_TERRAIN_H_INCLUDED )
